/*
 * Copyright (C) 2010-2011 Graz University of Technology, Austria. All Rights reserved. 
 *
 * Contact: Simone Kopeinik <simone.kopeinik@tugraz.at>
 * 	   Graz University of Technology, Knowledge Management Institute, Brückenkopfgasse 1/6, 8020 Graz 	
 * 	   <http://www.kmi.tugraz.at/>
 * 
 * This software is part of the TARGET platform developed by the
 * TARGET EC-funded project www.reachyourtarget.org
 *
 * This software is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * The GNU General Public License is available at <http://www.gnu.org/licenses/>.
 *
 */
package at.tugraz.kmi.css.cbkst2.prereq.structurevis;

import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.ItemSelectable;
import java.awt.event.InputEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.ButtonGroup;
import javax.swing.Icon;
import javax.swing.JComboBox;
import javax.swing.JMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.plaf.basic.BasicIconFactory;

import org.apache.commons.collections15.Factory;

import at.tugraz.kmi.css.cbkst2.CbKSTCompetence;
import at.tugraz.kmi.css.cbkst2.PrerequesiteRelation;

import com.google.inject.Inject;

import edu.uci.ics.jung.visualization.RenderContext;
import edu.uci.ics.jung.visualization.annotations.AnnotatingGraphMousePlugin;
import edu.uci.ics.jung.visualization.control.AbstractModalGraphMouse;
import edu.uci.ics.jung.visualization.control.AnimatedPickingGraphMousePlugin;
import edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
import edu.uci.ics.jung.visualization.control.PickingGraphMousePlugin;
import edu.uci.ics.jung.visualization.control.RotatingGraphMousePlugin;
import edu.uci.ics.jung.visualization.control.ScalingGraphMousePlugin;
import edu.uci.ics.jung.visualization.control.ShearingGraphMousePlugin;
import edu.uci.ics.jung.visualization.control.TranslatingGraphMousePlugin;

final class HasseGraphMouse extends AbstractModalGraphMouse implements
		ModalGraphMouse, ItemSelectable {

	private CbKSTEditingGraphMousePlugin<CbKSTCompetence, PrerequesiteRelation>	editingPlugin;
	private CbKSTVertexEditingGraphMousePlugin									labelEditingPlugin;
	private CbKSTEditingPopupGraphMousePlugin									popupEditingPlugin;
	private AnnotatingGraphMousePlugin<CbKSTCompetence, PrerequesiteRelation>	annotatingPlugin;
	private final RenderContext<CbKSTCompetence, PrerequesiteRelation>			renderContext;
	private final Factory<CbKSTCompetence>										vertexFactory;
	private final Factory<PrerequesiteRelation>									edgeFactory;

	/**
	 * create an instance with passed values
	 * 
	 * @param vertexFactory
	 * @param edgeFactory
	 * 
	 * 
	 */
	@Inject
	public HasseGraphMouse(final Factory<CbKSTCompetence> vertexFactory,
			final Factory<PrerequesiteRelation> edgeFactory,
			RenderContext<CbKSTCompetence, PrerequesiteRelation> renderContext) {
		super(1.0f, 1.0f);
		this.vertexFactory = vertexFactory;
		this.edgeFactory = edgeFactory;
		this.renderContext = renderContext;
		loadPlugins();
		setModeKeyListener(new ModeKeyAdapter(this));
	}

	/**
	 * create the plugins, and load the plugins for TRANSFORMING mode
	 * 
	 */
	@Override
	protected void loadPlugins() {
		assert (renderContext != null);
		editingPlugin = new CbKSTEditingGraphMousePlugin<CbKSTCompetence, PrerequesiteRelation>(
				vertexFactory, edgeFactory);
		labelEditingPlugin = new CbKSTVertexEditingGraphMousePlugin();
		popupEditingPlugin = new CbKSTEditingPopupGraphMousePlugin(
				vertexFactory, edgeFactory);
		pickingPlugin = new PickingGraphMousePlugin<CbKSTCompetence, PrerequesiteRelation>();
		animatedPickingPlugin = new AnimatedPickingGraphMousePlugin<CbKSTCompetence, PrerequesiteRelation>();
		translatingPlugin = new TranslatingGraphMousePlugin(
				InputEvent.BUTTON1_MASK);
		scalingPlugin = new ScalingGraphMousePlugin(
				new CrossoverScalingControl(), 0, in, out);
		rotatingPlugin = new RotatingGraphMousePlugin();
		shearingPlugin = new ShearingGraphMousePlugin();
		annotatingPlugin = new AnnotatingGraphMousePlugin<CbKSTCompetence, PrerequesiteRelation>(
				renderContext);
		add(scalingPlugin);
		setMode(Mode.EDITING);
	}

	/**
	 * setter for the Mode.
	 */
	@Override
	public void setMode(Mode mode) {
		if (this.mode != mode) {
			fireItemStateChanged(new ItemEvent(this,
					ItemEvent.ITEM_STATE_CHANGED, this.mode,
					ItemEvent.DESELECTED));
			this.mode = mode;
			if (mode == Mode.TRANSFORMING) {
				setTransformingMode();
			} else if (mode == Mode.PICKING) {
				setPickingMode();
			} else if (mode == Mode.EDITING) {
				setEditingMode();
			} else if (mode == Mode.ANNOTATING) {
				setAnnotatingMode();
			}
			if (modeBox != null) {
				modeBox.setSelectedItem(mode);
			}
			fireItemStateChanged(new ItemEvent(this,
					ItemEvent.ITEM_STATE_CHANGED, mode, ItemEvent.SELECTED));
		}
	}

	@Override
	protected void setPickingMode() {
		remove(translatingPlugin);
		remove(rotatingPlugin);
		remove(shearingPlugin);
		remove(editingPlugin);
		remove(annotatingPlugin);
		add(pickingPlugin);
		add(animatedPickingPlugin);
		add(labelEditingPlugin);
		add(popupEditingPlugin);
	}

	@Override
	protected void setTransformingMode() {
		remove(pickingPlugin);
		remove(animatedPickingPlugin);
		remove(editingPlugin);
		remove(annotatingPlugin);
		add(translatingPlugin);
		add(rotatingPlugin);
		add(shearingPlugin);
		add(labelEditingPlugin);
		add(popupEditingPlugin);
	}

	protected void setEditingMode() {
		add(pickingPlugin);
		remove(animatedPickingPlugin);
		remove(translatingPlugin);
		remove(rotatingPlugin);
		remove(shearingPlugin);
		remove(annotatingPlugin);
		add(editingPlugin);
		add(labelEditingPlugin);
		add(popupEditingPlugin);
	}

	protected void setAnnotatingMode() {
		remove(pickingPlugin);
		remove(animatedPickingPlugin);
		remove(translatingPlugin);
		remove(rotatingPlugin);
		remove(shearingPlugin);
		remove(labelEditingPlugin);
		remove(editingPlugin);
		remove(popupEditingPlugin);
		add(annotatingPlugin);
	}

	/**
	 * @return the modeBox.
	 */
	@Override
	public JComboBox getModeComboBox() {
		if (modeBox == null) {
			modeBox = new JComboBox(
					new Mode[] { Mode.TRANSFORMING, Mode.PICKING, Mode.EDITING, Mode.ANNOTATING });
			modeBox.addItemListener(getModeListener());
		}
		modeBox.setSelectedItem(mode);
		return modeBox;
	}

	/**
	 * create (if necessary) and return a menu that will change the mode
	 * 
	 * @return the menu
	 */
	@Override
	public JMenu getModeMenu() {
		if (modeMenu == null) {
			modeMenu = new JMenu();// {
			Icon icon = BasicIconFactory.getMenuArrowIcon();
			modeMenu.setIcon(BasicIconFactory.getMenuArrowIcon());
			modeMenu.setPreferredSize(new Dimension(icon.getIconWidth() + 10,
					icon.getIconHeight() + 10));

			final JRadioButtonMenuItem transformingButton = new JRadioButtonMenuItem(
					Mode.TRANSFORMING.toString());
			transformingButton.addItemListener(new ItemListener() {
				@Override
				public void itemStateChanged(ItemEvent PrerequesiteRelation) {
					if (PrerequesiteRelation.getStateChange() == ItemEvent.SELECTED) {
						setMode(Mode.TRANSFORMING);
					}
				}
			});

			final JRadioButtonMenuItem pickingButton = new JRadioButtonMenuItem(
					Mode.PICKING.toString());
			pickingButton.addItemListener(new ItemListener() {
				@Override
				public void itemStateChanged(ItemEvent PrerequesiteRelation) {
					if (PrerequesiteRelation.getStateChange() == ItemEvent.SELECTED) {
						setMode(Mode.PICKING);
					}
				}
			});

			final JRadioButtonMenuItem editingButton = new JRadioButtonMenuItem(
					Mode.EDITING.toString());
			editingButton.addItemListener(new ItemListener() {
				@Override
				public void itemStateChanged(ItemEvent PrerequesiteRelation) {
					if (PrerequesiteRelation.getStateChange() == ItemEvent.SELECTED) {
						setMode(Mode.EDITING);
					}
				}
			});

			ButtonGroup radio = new ButtonGroup();
			radio.add(transformingButton);
			radio.add(pickingButton);
			radio.add(editingButton);
			transformingButton.setSelected(true);
			modeMenu.add(transformingButton);
			modeMenu.add(pickingButton);
			modeMenu.add(editingButton);
			modeMenu.setToolTipText("Menu for setting Mouse Mode");
			addItemListener(new ItemListener() {
				@Override
				public void itemStateChanged(ItemEvent PrerequesiteRelation) {
					if (PrerequesiteRelation.getStateChange() == ItemEvent.SELECTED) {
						if (PrerequesiteRelation.getItem() == Mode.TRANSFORMING) {
							transformingButton.setSelected(true);
						} else if (PrerequesiteRelation.getItem() == Mode.PICKING) {
							pickingButton.setSelected(true);
						} else if (PrerequesiteRelation.getItem() == Mode.EDITING) {
							editingButton.setSelected(true);
						}
					}
				}
			});
		}
		return modeMenu;
	}

	public static class ModeKeyAdapter extends KeyAdapter {
		private char				t	= 't';
		private char				p	= 'p';
		private char				e	= 'e';
		private char				a	= 'a';
		protected ModalGraphMouse	graphMouse;

		public ModeKeyAdapter(ModalGraphMouse graphMouse) {
			this.graphMouse = graphMouse;
		}

		public ModeKeyAdapter(char t, char p, char e, char a,
				ModalGraphMouse graphMouse) {
			this.t = t;
			this.p = p;
			this.e = e;
			this.a = a;
			this.graphMouse = graphMouse;
		}

		@Override
		public void keyTyped(KeyEvent event) {
			char keyChar = event.getKeyChar();
			if (keyChar == t) {
				((Component) event.getSource()).setCursor(Cursor
						.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
				graphMouse.setMode(Mode.TRANSFORMING);
			} else if (keyChar == p) {
				((Component) event.getSource()).setCursor(Cursor
						.getPredefinedCursor(Cursor.HAND_CURSOR));
				graphMouse.setMode(Mode.PICKING);
			} else if (keyChar == e) {
				((Component) event.getSource()).setCursor(Cursor
						.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
				graphMouse.setMode(Mode.EDITING);
			} else if (keyChar == a) {
				((Component) event.getSource()).setCursor(Cursor
						.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
				graphMouse.setMode(Mode.ANNOTATING);
			}
		}
	}

}
